package com.tang.lts.core.nio.idle;

import com.tang.lts.core.nio.channel.NioChannel;
import com.tang.lts.core.nio.channel.NioChannelImpl;
import com.tang.lts.core.nio.config.NioConfig;
import com.tang.lts.core.support.SystemClock;

import java.util.HashSet;
import java.util.concurrent.TimeUnit;

/**
 * @author Tang
 * @classname IdleDetector
 * @description [ 通道状态检测 ]
 * @date 2022/5/15 16:31
 */
public class IdleDetector {

    private final HashSet<NioChannel> channels = new HashSet<>();

    public void addChannel(NioChannel channel) {
        channels.add(channel);
    }

    public void removeChannel(NioChannel channel) {
        channels.remove(channel);
    }

    private DetectorTask task = new DetectorTask();

    public void start() {
        new Thread(task).start();
    }

    public void stop() {
        task.stop();
    }

    private class DetectorTask implements Runnable {

        private volatile boolean stop = false;
        private volatile Thread thread;

        @Override
        public void run() {
            thread = Thread.currentThread();

            while (!stop) {
                long currentTime = SystemClock.now();
                for (NioChannel channel : channels) {
                    if (channel.isConnected()) {
                        idleCheck0((NioChannelImpl) channel, currentTime);
                    }
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        private void idleCheck0(NioChannelImpl channel, long currentTime) {
            IdleInfo idleInfo = channel.getIdleInfo();
            long lastReadTime = idleInfo.getLastReadTime();
            long lastWriteIdleTime = idleInfo.getLastWriteIdleTime();

            long lastIoTime = Math.max(lastReadTime, lastWriteIdleTime);
            NioConfig config = channel.getConfig();
            // 多少秒没读写
            if (config.getIdleTimeBoth() > 0) {
                notifyIdle(channel, IdleState.BOTH_IDLE, currentTime, config.getIdleTimeBoth() * 1000,
                        Math.max(lastIoTime, config.getIdleTimeBoth()));
            }
            // 多少秒没读
            if (config.getIdleTimeRead() > 0) {
                notifyIdle(channel, IdleState.READER_IDLE, currentTime, config.getIdleTimeRead() * 1000,
                        Math.max(lastIoTime, config.getIdleTimeRead()));
            }
            // 多少秒没写
            if (config.getIdleTimeWrite() > 0) {
                notifyIdle(channel, IdleState.WRITER_IDLE, currentTime, config.getIdleTimeWrite() * 1000,
                        Math.max(lastIoTime, config.getIdleTimeWrite()));
            }
            notifyWriteTimeout(channel, currentTime);
        }

        private void notifyWriteTimeout(NioChannelImpl channel, long currentTime) {

        }

        private void notifyIdle(NioChannelImpl channel, IdleState idleState, long currentTime, long idleTime, long lastIotTime) {
            if ((idleTime > 0) && (lastIotTime != 0) && (currentTime - lastIotTime) >= idleTime) {
                channel.fireChannel(idleState, currentTime);
            }
        }

        public void stop() {
            stop = true;
            Thread thread = this.thread;
            if (thread != null) {
                thread.interrupt();
            }
        }

    }

}